/*
this application is to test a program and get some infomation about it,
input are src files , dat.in and dat.out
output are ans files , run time and memery used
add some security test
aother:yjy
<a>mailto:yjyfrombnu@gmail.com<a>  
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <iostream>
#include <map>
#include <string.h>
using namespace std;
const int SIZE = 1000;
char buff[SIZE];
bool init();
bool comp();
bool test();
bool cmp();
bool report();
map<string,string> op;
map<string,string> re;
int main(){
	if (init()&&comp()&&test()&&cmp()&&report()){
		printf("job done!\n");
	}else{
		printf("job failed\n");
	}
	return 0;
}
/*
init:
   prepar all datas
   1>src file
   2>test datas
*/

bool init(){
	FILE * f = fopen("job.lst","r");
	if (f==NULL){
		printf("can ont find file job.lst");
		return false;
	}
	char key[SIZE];
	char val[SIZE];
	op.clear();
	re.clear();
	while (fscanf(f,"%s%s",key,val)!=EOF){
		
		op[string(key)]=string(val);
		//printf("#%s    #%s\n",key,op[key].c_str());
	}
	int pages = getpagesize();
	//printf("page: %d\n",pages);
	sprintf(val,"%d",pages);
	op["pagesize"]=string(val);
	chdir(op["home"].c_str());
	return true;
}
/*
complie src files
complie time default 10s
*/
bool comp(){
	int ti;
	//strcpy(buff,op["complie"].c_str());
	sscanf(op["complie"].c_str(),"%d",&ti);
	//printf("complie time: %d\n",ti);
	rlimit tlim;
	tlim.rlim_cur = ti;
	tlim.rlim_max = ti;
	rusage info;
	char cmdcomp[SIZE];
	sprintf(cmdcomp,"g++ -o %s %s",op["bin"].c_str() , op["src"].c_str());
	//printf("cmd: %s\n",cmdcomp);
	int stat;
	pid_t cf=fork();
	/*
		here must use fork ,can not use vfork , if that op can not be used 
	*/
	if (cf==0){//child
		setrlimit(RLIMIT_CPU,&tlim);
		execlp(cmdcomp,cmdcomp,NULL);
		exit(0);
	}else{//parent
		wait4(cf,&stat,NULL,&info);
		if (stat!=0){
			return false;
		}
		printf("user: %ld.%06lds sys: %ld.%06lds\n",info.ru_utime.tv_sec,info.ru_utime.tv_usec,info.ru_stime.tv_sec,info.ru_stime.tv_usec);
		int ps;
		sscanf(op["pagesize"].c_str(),"%d",&ps);
		printf("pagesize: %d\n",ps);
		int memused = ps*info.ru_minflt;
		printf("mem used: %.2lfKB\n",memused/1024.0);
	}
	return true;					
}
/*
run bin files mk out datas
*/
bool test(){

	int ti;
	//cout<<"cao tl:"<<op["bin"].c_str() <<endl;
	sscanf(op["tl"].c_str(),"%d",&ti);
	printf("time limit: %d\n",ti);
	rlimit tlim;
	tlim.rlim_cur = ti;
	tlim.rlim_max = ti;
	int mi;
	sscanf(op["ml"].c_str(),"%d",&mi);
	printf("mem limit: %d kb\n",mi);
	mi*=1024;
	rlimit mlim;
	mlim.rlim_cur = mi;
	mlim.rlim_max = mi;
	rusage info;
	char cmdtest[SIZE];
/*
	cout<<op["bin"]<<endl;
	cout<<op["datin"]<<endl;
	cout<<op["ans"]<<endl;
*/
	sprintf(cmdtest,"./%s",op["bin"].c_str());
	//,op["datin"].c_str(),op["ans"].c_str()
	printf("de: %s\n",cmdtest);
	int stat;
	pid_t cf = fork();
	if (cf==0){//child
		setrlimit(RLIMIT_CPU,&tlim);
		setrlimit(RLIMIT_AS,&mlim);
		freopen(op["datin"].c_str(),"r",stdin);
		freopen(op["ans"].c_str(),"w",stdout);
		execlp(cmdtest,cmdtest,NULL);
		exit(0);
	}else{//parent
		wait4(cf,&stat,NULL,&info);
		if (stat!=0){
			return false;
		}
		printf("user: %ld.%06lds sys: %ld.%06lds\n",info.ru_utime.tv_sec,info.ru_utime.tv_usec,info.ru_stime.tv_sec,info.ru_stime.tv_usec);
		int ps;
		sscanf(op["pagesize"].c_str(),"%d",&ps);
		//printf("ps : %d\n",ps);
		int memused = ps*info.ru_minflt;
		printf("mem used: %.2lfKB\n",memused/1024.0);
	}
	return true;					
}
/*
compare 2 files are they different
*/
bool cmp(){
	char cmddiff[SIZE];
	char opt[SIZE];
	sprintf(opt,"-a");
	sprintf(cmddiff,"diff %s %s %s >cmp.tmp",opt,op["ans"].c_str(),op["datout"].c_str());
	printf("cmd: %s\n",cmddiff);
	pid_t work = fork();
	if (work==0){
		//freopen("cmp.tmp","w",stdout);
		system(cmddiff);
		exit(0);
	}else{
		wait(NULL);
		struct stat info;
		if (stat("cmp.tmp",&info)<0){
			perror("cmp error:");
			return false;
		}else{
			if (info.st_size==0){
				printf("AC\n");
			}else{
				printf("WA\n");
			}
		}
			
	}
	return true;
}
bool report(){
	return true;
}
